﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Web.UI.WebControls;
using System.Xml.Serialization;
using Medianamik.Core;
using Medianamik.Globalization;
using Telerik.Web.UI;
using CheckBox = System.Web.UI.WebControls.CheckBox;
using DropDown = System.Web.UI.WebControls.DropDownList;

namespace Medianamik.UI.Controls
{
    public enum RecurrenceTypes
    {
        Daily,
        Weekly,
        Monthly,
        Yearly
    }

    public enum RecurrenceDays
    {
        None,
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday,
        Sunday
    }

    public enum RecurrenceEndTypes
    {
        Infinit,
        Date,
        Number
    }

    public class RecurringEventSelector : BaseMedianamikControl
    {
        protected DropDown _recurrenceTypeDropDown;
        protected RadDatePicker _startDate = new RadDatePicker();

        protected Panel _endDatePanel = new Panel();
        protected RadDatePicker _endDate = new RadDatePicker();
        protected RadTimePicker _time = new RadTimePicker();
        protected RadTimePicker _endTime = new RadTimePicker();

        protected RadioButtonList _endTypes = new RadioButtonList();

        protected CheckBox _monday = new CheckBox();
        protected CheckBox _tuesday = new CheckBox();
        protected CheckBox _wednesday = new CheckBox();
        protected CheckBox _thursday = new CheckBox();
        protected CheckBox _friday = new CheckBox();
        protected CheckBox _saturday = new CheckBox();
        protected CheckBox _sunday = new CheckBox();

        protected Dictionary<RecurrenceDays, CheckBox> _daysCheckboxes;

        protected Panel _dayPanel = new Panel();

        protected TextBox _sequence = new TextBox();

        protected Panel _recurrenceNumberPanel = new Panel();
        protected TextBox _recurrenceNumber = new TextBox();

        protected bool EnableInfinitEndType { get; set; }
        protected bool EnableDateEndTime { get; set; }

        public override bool AutoGenerateValidators
        {
            get
            {
                return false;
            }
            set
            {
                base.AutoGenerateValidators = value;
            }
        }

        public override object GetValue()
        {
            var values = new RecurringEvent
                       {
                           StartDate = (_startDate.SelectedDate.HasValue ? 
                                _startDate.SelectedDate.Value : _startDate.MinDate),
                           EndDate = (GetSelectedRecurrenceEndType() == RecurrenceEndTypes.Date ? 
                                _endDate.SelectedDate : null),
                           RecurrenceNumber = (GetSelectedRecurrenceEndType() == RecurrenceEndTypes.Number ? 
                                (int?)int.Parse(_recurrenceNumber.Text) : null),
                           Time = _time.SelectedDate,
                           EndTime = _endTime.SelectedDate,
                           Type = GetSelectedRecurrenceType(),
                           Sequence = int.Parse(_sequence.Text),
                           Day = DayPosition()
                       };

            return RecurringEvent.ToXml(values);
        }

        public override void SetValue(object value)
        {
            var xml = value as string;

            if (!string.IsNullOrEmpty(xml))
                LoadFieldsFromRecurringEvent(RecurringEvent.FromXml(xml));
        }

        private RecurrenceTypes GetSelectedRecurrenceType()
        {
            return (RecurrenceTypes)Enum.Parse(typeof(RecurrenceTypes), _recurrenceTypeDropDown.SelectedValue);
        }

        private RecurrenceEndTypes GetSelectedRecurrenceEndType()
        {
            return (RecurrenceEndTypes)Enum.Parse(typeof(RecurrenceEndTypes), _endTypes.SelectedValue);
        }

        private int DayPosition()
        {
            var type = GetSelectedRecurrenceType();

            if (type == RecurrenceTypes.Weekly)
            {
                foreach (var kvp in _daysCheckboxes)
                {
                    if (kvp.Value.Checked)
                    {
                        return (int)kvp.Key;
                    }
                }
            }
            else if (type == RecurrenceTypes.Monthly)
            {
                return _startDate.SelectedDate.Value.Day;
            }
            else if (type == RecurrenceTypes.Yearly)
            {
                return _startDate.SelectedDate.Value.DayOfYear;
            }

            return 0;
        }

        public override Type TypeOfValue
        {
            get { return typeof(string); }
        }

        protected override void CreateControlHierarchy(bool pUseDataSource)
        {
            InitializeRecurrenceType();
            InitializeDatePicker(_startDate);
            InitializeEndTypes();
            InitializeDatePicker(_endDate);
            InitializeRecurrenceNumber();
            InitializeTimePicker();
            InitializeEndTimePicker();
            InitializeDayPickerPanel();
            InitializeSequence();

            Controls.Add(GetBeginDiv());
            Controls.Add(CreateLabel(Translator.Translate("Medianamik_Controls", "RecurrenceType"))); ;
            Controls.Add(_recurrenceTypeDropDown);
            Controls.Add(GetEndDiv());

            Controls.Add(GetBeginDiv());
            Controls.Add(CreateLabel(Translator.Translate("Medianamik_Controls", "RecurrenceSquence")));
            Controls.Add(_sequence);
            Controls.Add(GetEndDiv());

            Controls.Add(GetBeginDiv());
            Controls.Add(CreateLabel(Translator.Translate("Medianamik_Controls", "RecurrenceStartDate"))); ;
            Controls.Add(_startDate);
            Controls.Add(GetEndDiv());

            Controls.Add(GetBeginDiv());
            Controls.Add(_endTypes);
            Controls.Add(GetEndDiv());

            Controls.Add(GetBeginDiv());
            Controls.Add(_endDatePanel);
            _endDatePanel.Controls.Add(_endDate);
            Controls.Add(GetEndDiv());

            Controls.Add(GetBeginDiv());
            Controls.Add(_recurrenceNumberPanel);
            _recurrenceNumberPanel.Controls.Add(_recurrenceNumber);
            Controls.Add(GetEndDiv());

            Controls.Add(GetBeginDiv());
            Controls.Add(CreateLabel(Translator.Translate("Medianamik_Controls", "RecurrenceTime")));
            Controls.Add(_time);
            Controls.Add(GetEndDiv());

            Controls.Add(GetBeginDiv());
            Controls.Add(CreateLabel(Translator.Translate("Medianamik_Controls", "RecurrenceToEndTime")));
            Controls.Add(_endTime);
            Controls.Add(GetEndDiv());

            Controls.Add(GetBeginDiv());
            Controls.Add(_dayPanel);
            Controls.Add(GetEndDiv());

            _startDate.ID = ControlToValidateId;

            if (pUseDataSource)
                SetValue(DataSource);
        }

        private Literal GetBeginDiv()
        {
            return new Literal { Text = "<div class=\"Control\">" };
        }

        private Literal GetEndDiv()
        {
            return new Literal { Text = "</div>" };

        }

        private void InitializeEndTypes()
        {
            if (EnableInfinitEndType)
            {
                _endTypes.Items.Add(new ListItem(Translator.Translate("Medianamik_Controls", "RecurrenceEndInfinit"),
                                                 RecurrenceEndTypes.Infinit.ToString()));
            }

            if (EnableDateEndTime)
            {
                _endTypes.Items.Add(new ListItem(Translator.Translate("Medianamik_Controls", "RecurrenceEndOnDate"),
                                                 RecurrenceEndTypes.Date.ToString()));
            }

            _endTypes.Items.Add(new ListItem(Translator.Translate("Medianamik_Controls", "RecurrenceEndNumber"),
                                             RecurrenceEndTypes.Number.ToString()));

            _endTypes.SelectedValue = RecurrenceEndTypes.Infinit.ToString();
            _endTypes.RepeatDirection = RepeatDirection.Horizontal;
            _endTypes.RepeatLayout = RepeatLayout.Flow;
            _endDatePanel.Visible = false;
            _recurrenceNumberPanel.Visible = false;
            _endTypes.AutoPostBack = true;

            _endTypes.SelectedIndexChanged += EndTypeChanged;
        }

        void EndTypeChanged(object sender, EventArgs e)
        {
            _endDatePanel.Visible = _endTypes.SelectedValue.Equals(RecurrenceEndTypes.Date.ToString());
            _recurrenceNumberPanel.Visible = _endTypes.SelectedValue.Equals(RecurrenceEndTypes.Number.ToString());
        }

        private void LoadFieldsFromRecurringEvent(RecurringEvent recurringEvent)
        {
            _recurrenceTypeDropDown.SelectedValue = recurringEvent.Type.ToString();
            _startDate.SelectedDate = recurringEvent.StartDate;
            _endDate.SelectedDate = recurringEvent.EndDate;
            _recurrenceNumber.Text = recurringEvent.RecurrenceNumber.ToString();
            _time.SelectedDate = recurringEvent.Time;
            _endTime.SelectedDate = recurringEvent.EndTime;
            _sequence.Text = recurringEvent.Sequence.ToString();
            
            if (recurringEvent.Type == RecurrenceTypes.Weekly)
            {
                var day = (RecurrenceDays)Enum.Parse(typeof (RecurrenceDays), recurringEvent.Day.ToString());
                _daysCheckboxes[day].Checked = true;
                _dayPanel.Visible = true;
            }

            if (recurringEvent.EndDate.HasValue)
            {
                _endTypes.SelectedValue = RecurrenceEndTypes.Date.ToString();
            }
            else if (recurringEvent.RecurrenceNumber.HasValue)
            {
                _endTypes.SelectedValue = RecurrenceEndTypes.Number.ToString();
            }

            EndTypeChanged(this, EventArgs.Empty);
        }

        private void InitializeRecurrenceNumber()
        {
            _recurrenceNumber.Text = "1";
            _recurrenceNumber.CssClass = "TxtBox";
        }

        private void InitializeSequence()
        {
            _sequence.Text = "1";
            _sequence.CssClass = "TxtBox";
        }

        private Literal CreateLabel(string text)
        {
            var label = new Literal
                            {
                                Text = string.Format("<label class=\"EditionPage\">{0}</label>", text)
                            };
            return label;
        }

        private void InitializeDayPickerPanel()
        {
            _daysCheckboxes = new Dictionary<RecurrenceDays, CheckBox>
                {
                    {RecurrenceDays.Monday, _monday},
                    {RecurrenceDays.Tuesday, _tuesday},
                    {RecurrenceDays.Wednesday, _wednesday},
                    {RecurrenceDays.Thursday, _thursday},
                    {RecurrenceDays.Friday, _friday},
                    {RecurrenceDays.Saturday, _saturday},
                    {RecurrenceDays.Sunday, _sunday}
                };

            _dayPanel.Visible = false;

            _dayPanel.Controls.Add(CreateLabel(Translator.Translate("Medianamik_Controls", "RecurrenceDay"))); ;
            foreach (var kvp in _daysCheckboxes)
            {
                kvp.Value.Text = Translator.Translate("Medianamik_Controls", kvp.Key.ToString());
                kvp.Value.CssClass = "CheckBox";
                _dayPanel.Controls.Add(kvp.Value);
            }
        }

        private void RecurrenceTypeChange(object sender, EventArgs e)
        {
            _dayPanel.Visible = _recurrenceTypeDropDown.SelectedValue
                .Equals(RecurrenceTypes.Weekly.ToString());
        }

        private void InitializeRecurrenceType()
        {
            _recurrenceTypeDropDown = new DropDown
                                     {
                                         DataSource = new Dictionary<RecurrenceTypes, string>
                                                          {
                                                              {RecurrenceTypes.Daily, Translator.Translate("medianamik_controls", "RecurringDaily")},
                                                              {RecurrenceTypes.Weekly, Translator.Translate("medianamik_controls", "RecurringWeekly")},
                                                              {RecurrenceTypes.Monthly, Translator.Translate("medianamik_controls", "RecurringMonthly")},
                                                              {RecurrenceTypes.Yearly, Translator.Translate("medianamik_controls", "RecurringYearly")}
                                                          },
                                         AutoPostBack = true,
                                         DataTextField = "value",
                                         DataValueField = "key",
                                         CssClass = "SelectList"
                                     };
            _recurrenceTypeDropDown.DataBind();
            _recurrenceTypeDropDown.SelectedIndexChanged += RecurrenceTypeChange;
        }

        protected void InitializeDatePicker(RadDatePicker datePicker)
        {
            datePicker.MinDate = new DateTime(1901, 1, 1);
            //Cette traduction ne fonctionne pas bien avec les resources de Telerik, override
            datePicker.DatePopupButton.ToolTip = Translator.Translate("Medianamik_Controls", "OpenCalendarPopup");
            datePicker.Calendar.FastNavigationSettings.CancelButtonCaption = Translator.Translate("Medianamik_Controls", "CancelButton");
            datePicker.Calendar.FastNavigationSettings.TodayButtonCaption = Translator.Translate("Medianamik_Controls", "TodayButton");
            datePicker.Calendar.ShowRowHeaders = false;
            datePicker.Calendar.UseColumnHeadersAsSelectors = false;
            datePicker.Calendar.EnableMonthYearFastNavigation = false;
            datePicker.DateInput.CssClass = "TxtBox";
            datePicker.Culture = MedianamikContext.Current.CurrentUICulture;

            //datePicker.EnableEmbeddedSkins = false;
            //datePicker.Skin = "Medianamik";
            //datePicker.CssClass = "DatePicker";

        }

        protected void InitializeTimePicker()
        {
            _time.MinDate = new DateTime(1901, 1, 1);
            _time.TimePopupButton.ToolTip = Translator.Translate("Medianamik_Controls", "OpenTimePopup");
            _time.DateInput.CssClass = "TxtBox";
            _time.Culture = MedianamikContext.Current.CurrentUICulture;

            _time.EnableEmbeddedSkins = false;
            _time.Skin = "Medianamik";
        }
        
        protected void InitializeEndTimePicker()
        {
            _endTime.MinDate = new DateTime(1901, 1, 1);
            _endTime.TimePopupButton.ToolTip = Translator.Translate("Medianamik_Controls", "OpenTimePopup");
            _endTime.DateInput.CssClass = "TxtBox";
            _endTime.Culture = MedianamikContext.Current.CurrentUICulture;

            _endTime.EnableEmbeddedSkins = false;
            _endTime.Skin = "Medianamik";
        }
    }

    [XmlRoot("Event")]
    public struct RecurringEvent
    {
        public static RecurringEvent FromXml(string xml)
        {
            if (string.IsNullOrEmpty(xml))
                return new RecurringEvent();

            var serializer = new XmlSerializer(typeof (RecurringEvent));
            var recurringEvent = (RecurringEvent)serializer.Deserialize(new StringReader(xml));

            if (recurringEvent.EndDate.HasValue)
            {
                recurringEvent.OccurencesDates = GetOccurrencesDates(recurringEvent.Type, recurringEvent.StartDate,
                                                                     recurringEvent.Sequence.Value,
                                                                     recurringEvent.EndDate.Value).ToList();
            }
            else if (recurringEvent.RecurrenceNumber.HasValue)
            {
                recurringEvent.OccurencesDates = GetOccurrencesDates(recurringEvent.Type, recurringEvent.StartDate,
                                                     recurringEvent.Sequence.Value,
                                                     recurringEvent.RecurrenceNumber.Value).ToList();

            }

            return recurringEvent;
        }

        public static string ToXml(RecurringEvent instance)
        {
            var serializer = new XmlSerializer(typeof (RecurringEvent));
            var stream = new StringWriter();
            serializer.Serialize(stream, instance);

            return stream.ToString();
        }

        public static IEnumerable<DateTime> GetOccurrencesDates(RecurrenceTypes type, 
            DateTime startDate, int sequence, int occurenceNumber)
        {
            var occurenceDate = startDate;
            var i = 0;

            while (i < occurenceNumber)
            {
                yield return occurenceDate;
                occurenceDate = GetNextOccurenceDate(occurenceDate, type, sequence);
                i++;
            }
        }

        public static IEnumerable<DateTime> GetOccurrencesDates(RecurrenceTypes type, 
            DateTime startDate, int sequence, DateTime endDate)
        {
            var occurenceDate = startDate;

            while (occurenceDate < endDate)
            {
                yield return occurenceDate;
                occurenceDate = GetNextOccurenceDate(occurenceDate, type, sequence);
            }
        }

        private static DateTime GetNextOccurenceDate(DateTime occurenceDate, RecurrenceTypes type, int sequence)
        {
            if (type == RecurrenceTypes.Daily)
            {
                occurenceDate = occurenceDate.AddDays(sequence);
            }
            else if (type == RecurrenceTypes.Weekly)
            {
                occurenceDate = occurenceDate.AddDays(7 * sequence);
            }
            else if (type == RecurrenceTypes.Monthly)
            {
                occurenceDate = occurenceDate.AddMonths(sequence);
            }
            else if (type == RecurrenceTypes.Yearly)
            {
                occurenceDate = occurenceDate.AddYears(sequence);
            }
            return occurenceDate;
        }

        [XmlElement]
        public DateTime StartDate { get; set; }

        [XmlElement]
        public DateTime? EndDate { get; set; }

        [XmlElement]
        public int? RecurrenceNumber { get; set; }

        [XmlElement]
        public DateTime? Time { get; set; }

        [XmlElement]
        public DateTime? EndTime { get; set; }

        [XmlElement]
        public RecurrenceTypes Type { get; set; }

        [XmlElement]
        public int? Day { get; set; }

        [XmlElement]
        public int? Sequence { get; set; }

        [XmlIgnore]
        public IList<DateTime> OccurencesDates { get; set; }
    }
}
